package com.tyrone.gateway.server;


import com.tyrone.gateway.common.GatewayException;
import com.tyrone.gateway.handler.HandlerManager;
import com.tyrone.gateway.registry.InstanceInfo;
import com.tyrone.gateway.registry.LoadBalanceEnum;
import com.tyrone.gateway.registry.RegistryFactory;
import com.tyrone.gateway.route.bean.RequestInfo;
import com.tyrone.gateway.route.bean.RouteInfo;
import com.tyrone.gateway.route.filter.RequestFilter;
import com.tyrone.gateway.transport.DispatchClient;
import com.tyrone.gateway.transport.Invoke;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE;
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;

@Slf4j
public class ServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static final byte[] CONTENT = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};

    private static final HttpDataFactory factory =
            new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);

    private RequestFilter requestFilter = new RequestFilter();

    private HandlerManager handlerManager = new HandlerManager();

    private RegistryFactory registryFactory = new RegistryFactory();

    private DispatchClient dispatchClient = new DispatchClient();

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    public static void main(String[] args) {
        QueryStringDecoder queryStringDecoder = new QueryStringDecoder("/testUri?name=tyrone,cat&age=1&age=10");
        String s = queryStringDecoder.rawPath();
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) {

        QueryStringDecoder decoder = new QueryStringDecoder(msg.uri());
        Map<String, List<String>> params = decoder.parameters();

        ByteBuf byteBuf = msg.content();
        String body = byteBuf.toString(StandardCharsets.UTF_8);


        /**
         * 构造请求信息
         */
        RequestInfo requestInfo = RequestInfo.builder()
                .method(msg.method().name())
                .rawPath(decoder.rawPath())
                .uri(msg.uri())
                .urlParams(params)
                .body(StringUtil.isNullOrEmpty(body) ? null : body)
                .build();

        RouteInfo routeInfo = requestFilter.filter(requestInfo);
        /**
         * 交给过滤器
         */
        handlerManager.preHandle(routeInfo);

        //todo 这里的Instance 创建 后面需要交给算力中心去操作

        InstanceInfo instanceInfo = null;

        if (routeInfo.getLoadBalanceType() != null &&
                LoadBalanceEnum.DEFAULT_WHIT_REGISTRY.code.equals(routeInfo.getLoadBalanceType())) {
            instanceInfo = registryFactory.build().selectOneHealthyInstance(routeInfo.convertToServiceInfo());
        }

        if (instanceInfo == null) {
            log.error("can not find an available instance");
            throw new GatewayException("can not find an available instance");
        }

        FullHttpResponse response = dispatchClient.call(Invoke.buildInvoke(instanceInfo));


        handlerManager.postHandler(routeInfo);


        boolean keepAlive = HttpUtil.isKeepAlive(msg);

        if (keepAlive) {
            if (!msg.protocolVersion().isKeepAliveDefault()) {
                response.headers().set(CONNECTION, KEEP_ALIVE);
            }
        } else {
            // Tell the client we're going to close the connection.
            response.headers().set(CONNECTION, CLOSE);
        }

        ChannelFuture f = ctx.write(response);

        if (!keepAlive) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
